Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make certain panicky stdlib functions behave better under panic_immediate_abort #91737

Merged
merged 3 commits into from
Dec 12, 2021

Conversation

Manishearth
Copy link
Member

The stdlib has a panic_immediate_abort feature that turns panics into immediate aborts, without any formatting/display logic. This feature was introduced primarily for codesize-constrained situations.

Unfortunately, this win doesn't quite propagate to Result::expect() and Result::unwrap(), while the formatting machinery is reduced, expect() and unwrap() both call unwrap_failed("msg", &err) which has a signature of fn unwrap_failed(msg: &str, error: &dyn fmt::Debug) and is #[inline(never)]. This means that unwrap_failed will unconditionally construct a dyn Debug trait object even though the object is never used in the function.

Constructing a trait object (even if you never call a method on it!) forces rust to include the vtable and any dependencies. This means that in panic_immediate_abort mode, calling expect/unwrap on a Result will pull in a whole bunch of formatting code for the error type even if it's completely unused.

This PR swaps out the function with one that won't require a trait object such that it won't force the inclusion of vtables in the code. It also gates off #[inline(never)] in a bunch of other places where allowing the inlining of an abort may be useful (this kind of thing is already done elsewhere in the stdlib).

I don't know how to write a test for this; we don't really seem to have any tests for panic_immediate_abort anyway so perhaps it's fine as is.

@rust-highfive
Copy link
Collaborator

r? @joshtriplett

(rust-highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Dec 10, 2021
@joshtriplett
Copy link
Member

Looks reasonable to me.

One way to write a test for this would be to build an executable that calls certain functions, look at the (demangled) symbols it references, and verify that the formatting machinery isn't present.

But I think such tests could go in another PR.

@bors r+

@bors
Copy link
Contributor

bors commented Dec 10, 2021

📌 Commit 917dafc has been approved by joshtriplett

@bors
Copy link
Contributor

bors commented Dec 10, 2021

🌲 The tree is currently closed for pull requests below priority 100. This pull request will be tested once the tree is reopened.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Dec 10, 2021
@eddyb
Copy link
Member

eddyb commented Dec 10, 2021

One way to write a test for this would be to build an executable that calls certain functions

Do we have support for compiletest to do Cargo invocations? Maybe through run-make?

(I assume this requires the Cargo -Z build-std and -Z build-std-features=... flags)

Comment on lines +1659 to 1661
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[cold]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does #[inline] have any effect when it's also #[cold]?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea! We could gate that attribute too, I guess

#[inline(never)]
#[cold]
#[track_caller]
fn unwrap_failed(msg: &str, error: &dyn fmt::Debug) -> ! {
panic!("{}: {:?}", msg, error)
}

// This is a separate function to avoid constructing a `dyn Debug`
// that gets immediately thrown away, since vtables don't get cleaned up
// by dead code elimination if a trait object is constructed even if it goes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a limitation of LLVM, or is it something that could be addressed on our end?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this can be addressed on our end because it's something that has to be run after the rest of the optimizations

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't #[inline] enough? Would be useful to look at the optimized LLVM IR that still contains the vtable (for something that doesn't really have any code other than what's rooted by the vtable), and take stock of what's exported from the LLVM module.

I am pretty sure LLVM can handle removing unused fn params, but if a global (static/fn) isn't marked private/internal/etc. to avoid export, then the ABI demands its signature be unchanged (and potentially even non-inlining interprocedural optimizations might be less effective, but I'm not sure if LLVM considers an exported definition to be opaque in any way, I kinda doubt it).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eddyb In my experience LLVM is eager to keep around vtable specs that are never used. I think it treats the construction of a dyn and its use differently and if one goes away it doesn't necessarily figure out the other should too.

bors added a commit to rust-lang-ci/rust that referenced this pull request Dec 12, 2021
…askrgr

Rollup of 8 pull requests

Successful merges:

 - rust-lang#90081 (Make `intrinsics::write_bytes` const)
 - rust-lang#91643 (asm: Allow using r9 (ARM) and x18 (AArch64) if they are not reserved by the current target)
 - rust-lang#91737 (Make certain panicky stdlib functions behave better under panic_immediate_abort)
 - rust-lang#91750 (rustdoc: Add regression test for Iterator as notable trait on &T)
 - rust-lang#91764 (Do not ICE when suggesting elided lifetimes on non-existent spans.)
 - rust-lang#91780 (Remove hir::Node::hir_id.)
 - rust-lang#91797 (Fix zero-sized reference to deallocated memory)
 - rust-lang#91806 (Make `Unique`s methods `const`)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 90eb610 into rust-lang:master Dec 12, 2021
@rustbot rustbot added this to the 1.59.0 milestone Dec 12, 2021
@Manishearth Manishearth deleted the panic-immediate-stdlib branch December 12, 2021 06:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants